package snsdbook.engine.message;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

import snsdbook.engine.FileManager;
import util.SQLUtil;
import util.Util;

/**
 * Requests to synchronize with server.
 * 
 * @author dolphinigle
 */
public class SynchronizeRequestMessage implements MessageInterface {

  private static final long serialVersionUID = -6119569309345849465L;
  private String username_;
  private String[] has_;
  private long[] has_time_;
  private String[] deleted_fname_;
  private long[] deleted_time_;
  
  public SynchronizeRequestMessage(
      String username, String[] has, long[] has_time,
      String[] deleted, long[] deleted_time) {
    username_ = username;
    has_ = has;
    has_time_ = has_time;
    deleted_fname_ = deleted;
    deleted_time_ = deleted_time;
  }
  
  @Override
  public String ProcessAsServer(ObjectInputStream is, ObjectOutputStream os) {
    long[] timestamps = new long[has_time_.length + deleted_time_.length];
    String[] filenames = new String[has_.length + deleted_fname_.length];
    boolean[] deleted = new boolean[has_.length + deleted_fname_.length];
    for (int i = 0; i < has_.length; ++i) {
      timestamps[i] = has_time_[i];
      filenames[i] = has_[i];
      deleted[i] = false;
    }
    int n = has_.length;
    for (int i = 0; i < deleted_fname_.length; ++i) {
      timestamps[i + n] = deleted_time_[i];
      filenames[i + n] = deleted_fname_[i];
      deleted[i + n] = true;
    }
    n = timestamps.length;
    
    String[] ids = SQLUtil.Query(
        "SELECT id_file FROM files WHERE username='" + username_ + "'");
    String[] sfiles = SQLUtil.Query(
        "SELECT filepath FROM files WHERE username='" + username_ + "'");
    String[] stime_raw = SQLUtil.Query(
        "SELECT timestamp FROM files WHERE username='" + username_ + "'");
    String[] sdel = SQLUtil.Query(
        "SELECT deleted FROM files WHERE username='" + username_ + "'");

    int m = sfiles.length;
    
    long[] stime = new long[stime_raw.length];
    for (int i = 0; i < m; ++i) stime[i] = Long.valueOf(stime_raw[i]);
    
    ArrayList<MessageInterface> requests = new ArrayList<MessageInterface>();
    
    // Update the server
    for (int i = 0; i < n; ++i) {
      // Check this file is the newest
      boolean ok = true;
      for (int j = 0; j < n; ++j) {
        if (timestamps[j] > timestamps[i] && filenames[j].equals(filenames[i])) {
          ok = false;
          break;
        }
      }
      if (!ok) continue;
      
      for (int j = 0; j < m; ++j) {
        if (stime[j] >= timestamps[i] && sfiles[j].equals(filenames[i])) {
          // Server version is newer
          ok = false;
        }
      }
      if (!ok) continue;
      
      Util.Log("New file detected at user: " + filenames[i]);
      
      if (deleted[i]) {
        Util.Log("File was deleted at user.");
        // Oh, file was only deleted, let's delete it from server
        
        String[] id = SQLUtil.Query("SELECT id_file FROM files WHERE " +
            "username='" + username_ + "' AND filepath='" + filenames[i] + "'");
        if (id.length == 0) {
          Util.Log("File not found at server, probably user cr8 and delete it");
          // File didn't exist anyway...
        } else {
          assert id.length == 1;
          Util.Log("Marking file as deleted...");
          SQLUtil.Modify("UPDATE files SET timestamp='" + timestamps[i] + "', " +
              "deleted='1' WHERE id_file='" + id[0] + "'");
        }
      } else {
        Util.Log("Requesting user to upload his version...");
        String[] id = SQLUtil.Query("SELECT id_file FROM files WHERE " +
            "username='" + username_ + "' AND filepath='" + filenames[i] + "'");
        String yes_id = "-1";
        if (id.length == 1) yes_id = id[0];
        requests.add(new UploadFileRequest(filenames[i], yes_id, username_));
      }
    }
    
    // Update the client
    for (int i = 0; i < m; ++i) {
      boolean ok = true;
      for (int j = 0; j < n; ++j) {
        if (stime[i] <= timestamps[j] && sfiles[i].equals(filenames[j])) {
          // Client's version is newer
          ok = false;
        }
      }
      
      if (!ok) continue;
      
      Util.Log("Client's version of file " + sfiles[i] + " is not the newest");
      // This file looks new, what should we do?
      if (sdel[i].equals("1")) {
        // File is deleted, we should delete user's file too...
        // ... except if he doesn't have it
        ok = false;
        for (int j = 0; j < n; ++j) {
          if (filenames[j].equals(sfiles[i])) {
            ok = true;
            break;
          }
        }
        if (ok) {
          Util.Log("Requesting user to delete his file...");
          requests.add(new DeleteOrderMessage(sfiles[i]));
        }
      } else {
        // File should be updated
        Util.Log("Requesting user to update his file...");
        requests.add(new UpdateOrderMessage(
            sfiles[i], SQLUtil.GetBytes(ids[i]), stime[i]));
      }
    }
    
    new SynchronizeReplyMessage(requests.size()).ProcessAsServer(is, os);
    
    for (MessageInterface msg : requests) {
      msg.ProcessAsServer(is, os);
    }
    
    return "";
  }

  @Override
  public String ProcessAsClient(ObjectInputStream is, ObjectOutputStream os,
                                FileManager manager) {
    Util.Send(os, this);
    Util.Receive(is).ProcessAsClient(is, os, manager);
    return "";
  }
  
  @Override
  public String toString() {
    return "SYNCHRONIZE REQUEST, USER=" + username_ + ", FILES_CNT=" +
           has_.length + ", DEL_CNT=" + deleted_fname_.length;
  }

}
